#!/usr/bin/env python3
#
# This script reads the original Z80 PLA table and processes it to get the list of PLA
# entries in the form suitable to load into the PLA checker program.
#
# Format of the output PLA table:
# 7 bits of modifiers:
#  loc 0  If "1", IX or IY flag is reset        |\
#      1  If "1", IX or IY flag is set          | Only 1 of these 2 can be set
#      2  If "1", in HALT state
#      3  If "1", ALU operation
#      4  If "1", XX regular instruction        |\
#      5  If "1", CB instruction table modifier | Only 1 of these 3 can be set
#      6  If "1", ED instruction table modifier |/
#
#  Following 16 bits of opcode test in pairs, from bit7 to bit0
#    For each pair, if the left bit is "1", the opcode bit has to be 0
#                   if the right bit is "1", the opcode bit has to be 1
#                   otherwise the bit is ignored
#
#-------------------------------------------------------------------------------
#  Copyright (C) 2014  Goran Devic
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the Free
#  Software Foundation; either version 2 of the License, or (at your option)
#  any later version.
#
#  This program is distributed in the hope that it will be useful, but WITHOUT
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
#  more details.
#-------------------------------------------------------------------------------
import string
import os

# Input PLA table: a text file representing the original (Z80 reverse-engineered)
# table at http://arcfn.com/files/z80-pla-table.html
inFile = "z80-pla-original.txt"

# Output processed PLA table
outFile = "z80-pla.txt"

table = []

def xtostr ( condition ):
    if condition:
        return "1"
    return "."

# Read the content of the input PLA table and add all valid lines (starting with a number)
with open(inFile, 'r') as fIn:
    for line in fIn:
        seg = line.split()
        if len(seg)>1 and seg[0].isdigit():
            table.append(line)

# In the original PLA table, entries are listed in reverse order -
table.reverse()

# Control ID to detect errors
controlID = 0

with open(outFile, 'w') as fOut:
    fOut.write("# Automatically generated by process-pla.py\n")
    pla = []
    for entry in table:
        # This is an example of an input PLA entry containing different segments:
        # ID   ALU  XX  CB  ED  0  /0   1  /1   2  /2   6  /6   7  /7   3  /3   4  /4   5  /5   Instruction Ex  Description
        # 59    1   1   .   .   .   .   .   .   .   .   .   1   1   .   1   .   .   1   .   1   01110xxx    ce  ld (hl),r (and hlt)
        # 0     1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20   21          22  23
        seg = entry.split()
        if len(seg)>22:
            # Sanity check
            if controlID!=int(seg[0]):
                fOut.write("Unexpected entry " + seg[0])
                exit(-1)
            controlID = controlID + 1

            # Decipher [22] into actual PLA entry lines IXY0, IXY1 and HALT (a, b, c)
            entry = ""
            entry += xtostr("a" in seg[22])
            entry += xtostr("b" in seg[22])
            entry += xtostr("c" in seg[22])
            # Append the rest of the PLA modifiers: ALU, XX, CB and ED
            entry += seg[1] + seg[2] + seg[3] + seg[4] + ' '
            # Add the instruction decoder bit7 through bit0 in that order
            #         7      /7        6      /6        5      /5       4       /4        3      /3        2     /2        1     /1       0     /0
            entry += seg[13]+seg[14] +seg[11]+seg[12] +seg[19]+seg[20] +seg[17]+seg[18] +seg[15]+seg[16] +seg[9]+seg[10] +seg[7]+seg[8] +seg[5]+seg[6]

            # Check if the new PLA entry is a duplicate (already stored to PLA)
            # Append "D" for duplicate entries so they can be skipped easily
            if entry in pla:
                fOut.write(entry + "\tD\t")
            else:
            # Store the new PLA line in the PLA list and write it out
                pla.append(entry)
                fOut.write(entry + "\t-\t")

            # Write the ID number, text decode and description
            fOut.write(seg[0] + "\t" + seg[21] + "\t" + ' '.join(seg[23:len(seg)]) +  "\n")

    # The original PLA table passes individual opcode bits that are needed to decode many
    # instructions but it does not contain concrete single-bit entries
    # We append those bits to the end of our PLA table
    fOut.write("....... ...............1\t-\t99\txxxxxxx1\topcode[0]\n")
    fOut.write("....... .............1..\t-\t100\txxxxxx1x\topcode[1]\n")
    fOut.write("....... ...........1....\t-\t101\txxxxx1xx\topcode[2]\n")
    fOut.write("....... .........1......\t-\t102\txxxx1xxx\topcode[3]\n")
    fOut.write("....... .......1........\t-\t103\txxx1xxxx\topcode[4]\n")
    fOut.write("....... .....1..........\t-\t104\txx1xxxxx\topcode[5]\n")
